package com.github.cyf.log;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @author chenyifan
 * @create 2025-01-10 15:02
 */
@Aspect
@Component
@Slf4j
public class HttpLogAspect {
    /**
     * 拦截所有controller包下的方法
     */
    @Pointcut("@annotation(com.github.cyf.log.HttpLog)")
    private void controllerMethod() {
    }

    @Around("controllerMethod()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        // 获取请求信息
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String url = request.getRequestURL().toString();
        String method = request.getMethod();

        // 获取请求方法信息
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        String params = handleParams(args, u.getParameterNames(signature.getMethod()));
        String className = signature.getDeclaringTypeName();
        String methodName = signature.getName();
        log.info("@http请求开始, {}#{}(), method: {}, URL: {}, params: {}", className, methodName, method, url, params);
        //result的值就是被拦截方法的返回值
        try {
            //proceed方法是调用实际所拦截的controller中的方法，这里的result为调用方法后的返回值
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            //定义请求结束时的返回数据，包括调用时间、返回值结果等
            log.info("@http请求结束, {}#{}(), method: {}, URL: {}, time: {}ms ", className, methodName, method, url, (endTime - startTime));
            return result;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            log.error("@http请求出错, {}#{}(), URI: {}, method: {}, URL: {}, time: {}ms", className, methodName, method, url, (endTime - startTime), e);
            throw e;
        }
    }

    /**
     * 参数打印合理化
     *
     * @param args       参数列表
     * @param paramNames 参数名
     * @return
     */
    private String handleParams(Object[] args, Object[] paramNames) {
        Map map = new HashMap();
        for (int i = 0; i < args.length; i++) {
            Object paramName = paramNames[i];
            Object paramVal = args[i];
            if (paramVal instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) paramVal;
                map.put(paramName, file.getOriginalFilename());
            } else {
                map.put(paramName, paramVal);
            }
        }
        return map.toString();
    }
}